﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Core.Module.Interfaces;
using Core.Module.Interfaces.Manager;
using Core.Module.Interfaces.Model;
using umbraco.BusinessLogic.Utils;
using System.Xml;
using umbraco;
using System.IO;

namespace Core.Module.Manager
{
    public class ModuleManager : IModuleManager<IFrontend>
    {
        List<Type> modules;

        private string FilePath
        {
            get
            {
                return string.Concat(new object[] { GlobalSettings.FullpathToRoot, Path.DirectorySeparatorChar, "config", Path.DirectorySeparatorChar, "umbracoSettings.config" });
            }
        }

        public List<Type> Get()
        {
            if (modules == null)
                lock (modules = new List<Type>())
                {
                    modules = TypeFinder.FindClassesOfType<IModule>();
                }
            return modules;
        }

        public List<IModule> Get(ISite forSite)
        {
            return Get()
                .Select(mti => Activator.CreateInstance(mti, null, null) as IModule)
                .Where(mi => EnsureModuleSettings(mi, forSite))
                .OrderBy(mi=>mi.Enabled)
                .ThenBy(mi => mi.Position)
                .ToList();
        }

        private bool EnsureModuleSettings(IModule module, ISite forSite)
        {
            XmlElement moduleSettings = (XmlElement)umbraco.UmbracoSettings.GetKeyAsNode(string.Format("ism/sites/site[@id='{0}' or @name='{1}']/modules/module[@alias='{3}' or @alias='{4}' or @id='{5}']", forSite.Node.Id, forSite.Node.Name, forSite.Node.Title, module.Alias, module.Alias.ToLowerInvariant(), module.Id));
            if (moduleSettings == null)
            {
                SetDefaults(module);
                UpdateSettings(module, forSite);
            }
            else
            {
                module.Enabled = moduleSettings.GetAttribute("enable") == "0" ? false : true;
                module.Active = moduleSettings.GetAttribute("active") == "0" ? false : true;
                int pos = 0;
                module.Position = int.TryParse(moduleSettings.GetAttribute("position"),out pos)?pos:1;                
                module.Settings = (XmlElement)moduleSettings.SelectSingleNode("settings");
            }
            if (module.Settings ==null || module.Settings.ChildNodes.Count==0) module.Settings = (XmlElement)umbraco.UmbracoSettings.GetKeyAsNode(string.Format("ism/modules/module[@alias='{0}' or @alias='{1}' or @id='{2}']/settings", module.Alias, module.Alias.ToLowerInvariant(), module.Id));
            return module.Active;
        }


        private bool SetDefaults(IModule module)
        {
            module.Enabled = true;
            module.Active = true;
            module.Position = 1;
            return true;
        }

        public IModule UpdateSettings(IModule data, ISite forSite)
        {

            lock (UmbracoSettings._umbracoSettings)
            {
                XmlDocument xdoc = UmbracoSettings._umbracoSettings; ///or by filepath property
                XmlElement modules = EnsureModulesSettings(xdoc, forSite);
                if (modules == null) return data;///don't update data,if site not exist
                XmlElement module = (XmlElement)modules.SelectSingleNode(string.Format("module[@alias='{0}']", data.Alias));
                if (module == null)
                {
                    module = xdoc.CreateElement("module");
                    modules.AppendChild(module);
                }
                module.SetAttribute("alias", data.Alias);
                module.SetAttribute("enable", data.Enabled ? "1" : "0");
                module.SetAttribute("active", data.Active ? "1" : "0");
                XmlElement el = EnsureElement(module, "settings", "settings");
                if (data.Settings == null)
                {
                    data.Settings = xdoc.CreateElement("settings");
                }
                el.InnerXml = data.Settings.InnerXml;
                xdoc.Save(FilePath);
            }

            return data;
        }

        private XmlElement EnsureModulesSettings(XmlDocument xdoc, ISite forSite)
        {
            if (forSite == null) return null;
            XmlElement root = (XmlElement)xdoc.SelectSingleNode("/*");
            XmlElement el = EnsureElement(root, "ism", "ism");
            el = EnsureElement(el, "sites", "sites");
            string key = string.Format("site[@id='{0}' or @name='{1}']", forSite.Node.Id, forSite.Node.Name.ToLowerInvariant(), forSite.Node.Title);
            el = EnsureElement(el, key, "site");
            el.SetAttribute("id", forSite.Node.Id);
            el.SetAttribute("name", forSite.Node.Name.ToLowerInvariant());

            el = EnsureElement(el, "modules", "modules");
            return el;
        }
        private XmlElement EnsureElement(XmlElement parent, string xpath, string nodeName)
        {
            XmlElement nds = (XmlElement)parent.SelectSingleNode(xpath);
            if (nds != null) return nds;
            nds = parent.OwnerDocument.CreateElement(nodeName);
            parent.AppendChild(nds);
            return nds;
        }
    }
}
